import * as THREE from 'three';
import { CONFIG } from '@/core/constants';
import { Vec3, ChunkPosition } from '@/core/types';
import { MathUtils } from '@/utils/math';
import { Chunk } from './Chunk';
import { ChunkMeshBuilder } from './ChunkMeshBuilder';
import { BlockType } from '@/blocks/types';

export class WorldManager {
  private scene: THREE.Scene;
  private chunks: Map<string, Chunk> = new Map();
  private chunkMeshBuilder: ChunkMeshBuilder;
  private lastPlayerChunk: ChunkPosition = { x: 0, z: 0 };

  constructor(scene: THREE.Scene) {
    this.scene = scene;
    this.chunkMeshBuilder = new ChunkMeshBuilder();
    
    // 立即生成初始区块
    this.generateInitialChunks();
  }

  private generateInitialChunks(): void {
    const playerChunk = { x: 0, z: 0 };
    
    console.log('Generating initial chunks...');
    
    for (let x = playerChunk.x - CONFIG.RENDER_DISTANCE; 
         x <= playerChunk.x + CONFIG.RENDER_DISTANCE; x++) {
      for (let z = playerChunk.z - CONFIG.RENDER_DISTANCE; 
           z <= playerChunk.z + CONFIG.RENDER_DISTANCE; z++) {
        
        const distance = Math.sqrt((x - playerChunk.x) ** 2 + (z - playerChunk.z) ** 2);
        if (distance <= CONFIG.RENDER_DISTANCE) {
          const chunkKey = this.getChunkKey({ x, z });
          if (!this.chunks.has(chunkKey)) {
            this.loadChunk({ x, z });
          }
        }
      }
    }
    
    console.log(`Generated ${this.chunks.size} chunks`);
    
    // 立即构建所有区块的网格
    this.updateChunkMeshes();
    
    console.log('Initial chunk meshes built');
  }

  public update(playerPosition: Vec3): void {
    const currentChunk = MathUtils.worldToChunk(playerPosition);
    
    if (currentChunk.x !== this.lastPlayerChunk.x || 
        currentChunk.z !== this.lastPlayerChunk.z) {
      this.updateChunks(currentChunk);
      this.lastPlayerChunk = currentChunk;
    }
    
    this.updateChunkMeshes();
  }

  private updateChunks(playerChunk: ChunkPosition): void {
    const chunksToLoad: ChunkPosition[] = [];
    const chunksToUnload: string[] = [];
    
    for (let x = playerChunk.x - CONFIG.RENDER_DISTANCE; 
         x <= playerChunk.x + CONFIG.RENDER_DISTANCE; x++) {
      for (let z = playerChunk.z - CONFIG.RENDER_DISTANCE; 
           z <= playerChunk.z + CONFIG.RENDER_DISTANCE; z++) {
        
        const distance = Math.sqrt((x - playerChunk.x) ** 2 + (z - playerChunk.z) ** 2);
        if (distance <= CONFIG.RENDER_DISTANCE) {
          const chunkKey = this.getChunkKey({ x, z });
          if (!this.chunks.has(chunkKey)) {
            chunksToLoad.push({ x, z });
          }
        }
      }
    }
    
    for (const [chunkKey, chunk] of this.chunks) {
      const distance = Math.sqrt(
        (chunk.position.x - playerChunk.x) ** 2 + 
        (chunk.position.z - playerChunk.z) ** 2
      );
      
      if (distance > CONFIG.RENDER_DISTANCE + 1) {
        chunksToUnload.push(chunkKey);
      }
    }
    
    chunksToLoad.forEach(pos => this.loadChunk(pos));
    chunksToUnload.forEach(key => this.unloadChunk(key));
  }

  private loadChunk(position: ChunkPosition): void {
    const chunkKey = this.getChunkKey(position);
    const chunk = new Chunk(position);
    this.chunks.set(chunkKey, chunk);
  }

  private unloadChunk(chunkKey: string): void {
    const chunk = this.chunks.get(chunkKey);
    if (chunk) {
      const mesh = chunk.getMesh();
      if (mesh) {
        this.scene.remove(mesh);
      }
      chunk.dispose();
      this.chunks.delete(chunkKey);
    }
  }

  private updateChunkMeshes(): void {
    for (const chunk of this.chunks.values()) {
      if (chunk.needsMeshUpdate()) {
        this.rebuildChunkMesh(chunk);
      }
    }
  }

  private rebuildChunkMesh(chunk: Chunk): void {
    const oldMesh = chunk.getMesh();
    if (oldMesh) {
      this.scene.remove(oldMesh);
    }
    
    const newMesh = this.chunkMeshBuilder.buildChunkMesh(chunk);
    if (newMesh) {
      this.scene.add(newMesh);
      chunk.setMesh(newMesh);
      } else {
      console.warn(`Failed to build mesh for chunk ${chunk.position.x},${chunk.position.z}`);
    }
  }

  private getChunkKey(position: ChunkPosition): string {
    return `${position.x},${position.z}`;
  }

  public getBlock(worldPos: Vec3): number {
    const chunkPos = MathUtils.worldToChunk(worldPos);
    const chunkKey = this.getChunkKey(chunkPos);
    const chunk = this.chunks.get(chunkKey);
    
    if (!chunk) return 0;
    
    const localX = Math.floor(worldPos.x) - chunkPos.x * CONFIG.CHUNK_SIZE;
    const localY = Math.floor(worldPos.y);
    const localZ = Math.floor(worldPos.z) - chunkPos.z * CONFIG.CHUNK_SIZE;
    
    return chunk.getBlock(localX, localY, localZ);
  }

  public setBlock(worldPos: Vec3, blockType: BlockType): void {
    const chunkPos = MathUtils.worldToChunk(worldPos);
    const chunkKey = this.getChunkKey(chunkPos);
    const chunk = this.chunks.get(chunkKey);
    
    if (!chunk) return;
    
    const localX = Math.floor(worldPos.x) - chunkPos.x * CONFIG.CHUNK_SIZE;
    const localY = Math.floor(worldPos.y);
    const localZ = Math.floor(worldPos.z) - chunkPos.z * CONFIG.CHUNK_SIZE;
    
    chunk.setBlock(localX, localY, localZ, blockType);
  }

  public getLoadedChunks(): Chunk[] {
    return Array.from(this.chunks.values());
  }
}